#pragma once
#include "GePoint2.h"
namespace tge{

	template<class PointT2, typename RealT>
	class TGeBoundBox2
	{
	public:
		PointT2 m_ptMin;
		PointT2 m_ptMax;
	public:
		TGeBoundBox2()
		{
			setEmpty();
		}
		TGeBoundBox2(RealT xmin, RealT ymin, RealT xmax, RealT ymax)
		{
			m_ptMin.set(xmin, ymin);
			m_ptMax.set(xmax, ymax);
		}
		const TGeBoundBox2& set(const PointT2&pt)
		{
			m_ptMin = pt;
			m_ptMax = pt;
			return *this;
		}
		TGeBoundBox2& set(RealT x, RealT y)
		{
			set(PointT2(x, y));
			return *this;
		}
		TGeBoundBox2& set(int vnum, const PointT2*arrVts)
		{
			ASSERT(vnum > 0 && arrVts);
			set(arrVts[0]);
			for (int i = 1; i < vnum; ++i)
			{
				extend(arrVts[i]);
			}
			return *this;
		}
		TGeBoundBox2& set(const PointT2&p1, const PointT2&p2)
		{
			set(p1);
			extend(p2);
			return *this;
		}
		TGeBoundBox2& set(RealT xmin, RealT ymin, RealT xmax, RealT ymax)
		{
			m_ptMin.set(xmin, ymin);
			m_ptMax.set(xmax, ymax);
			return *this;
		}
		TGeBoundBox2& extend(RealT x, RealT y)
		{
			if (empty())
			{
				set(x, y);
				return *this;
			}
			if (x < m_ptMin.x)m_ptMin.x = x;
			else if (x > m_ptMax.x)m_ptMax.x = x;
			if (y < m_ptMin.y)m_ptMin.y = y;
			else if (y > m_ptMax.y)m_ptMax.y = y;
			return *this;
		}
		TGeBoundBox2& extend(int vnum, const PointT2*arrVts)
		{
			ASSERT(vnum > 0);
			ASSERT(arrVts);
			if (arrVts == NULL)return *this;
			int i = 0;
			if (empty())
			{
				set(arrVts[0]);
				++i;
			}
			for (; i < vnum; ++i)
			{
				extend(arrVts[i]);
			}
			return *this;
		}
		TGeBoundBox2& extend(const PointT2&pt)
		{
			extend(pt.x, pt.y);
			return *this;
		}
		TGeBoundBox2& extend(const TGeBoundBox2&bb)
		{
			extend(bb.minPoint());
			extend(bb.maxPoint());
			return *this;
		}
		TGeBoundBox2& swell(RealT dist)
		{
			m_ptMin.x -= dist;
			m_ptMin.y -= dist;
			m_ptMax.x += dist;
			m_ptMax.y += dist;
			return *this;
		}
		TGeBoundBox2& normalize()
		{
			if (m_ptMin.x > m_ptMax.x)std::swap(m_ptMin.x, m_ptMax.x);
			if (m_ptMin.y > m_ptMax.y)std::swap(m_ptMin.y, m_ptMax.y);
			return *this;
		}
		TGeBoundBox2& setEmpty()
		{
			m_ptMin.set(1, 1);
			m_ptMax.set(-1, -1);
			return *this;
		}
		bool empty()const
		{
			return m_ptMin.x > m_ptMax.x;
		}
		const PointT2&minPoint()const
		{
			return m_ptMin;
		}
		const PointT2&maxPoint()const
		{
			return m_ptMax;
		}
		PointT2 center()const
		{
			return PointT2((m_ptMin.x + m_ptMax.x) / 2, (m_ptMin.y + m_ptMax.y) / 2);
		}
		void getSize(RealT&xlen, RealT&ylen)const
		{
			xlen = m_ptMax.x - m_ptMin.x;
			ylen = m_ptMax.y - m_ptMin.y;
		}
		bool disjoint(const TGeBoundBox2 &bb) const
		{
			if (bb.m_ptMin.x > m_ptMax.x || bb.m_ptMax.x<m_ptMin.x)return true;
			if (bb.m_ptMin.y>m_ptMax.y || bb.m_ptMax.y < m_ptMin.y)return true;
			return false;
		}
		bool contains(RealT x, RealT y)const
		{
			if (x < m_ptMin.x)return false;
			if (x > m_ptMax.x)return false;
			if (y < m_ptMin.y)return false;
			if (y > m_ptMax.y)return false;
			return true;
		}
		bool contains(const PointT2 &pt)const
		{
			if (pt.x < m_ptMin.x)return false;
			if (pt.x > m_ptMax.x)return false;
			if (pt.y < m_ptMin.y)return false;
			if (pt.y > m_ptMax.y)return false;
			return true;
		}
		bool contains(const TGeBoundBox2&bb)const
		{
			if (!contains(bb.minPoint()))return false;
			if (!contains(bb.maxPoint()))return false;
			return true;
		}
		void getPoints(PointT2 pts[4])const
		{
			pts[0].set(m_ptMin.x, m_ptMin.y);
			pts[1].set(m_ptMax.x, m_ptMin.y);
			pts[2].set(m_ptMax.x, m_ptMax.y);
			pts[3].set(m_ptMin.x, m_ptMax.y);
		}
		bool pointIn(RealT x, RealT y, RealT tol) const
		{
			if (x<m_ptMin.x - tol || y<m_ptMin.y - tol || x>m_ptMax.x + tol || y>m_ptMax.y + tol)return false;
			return true;
		}
		int clip_test(double p, double q, double *u1, double *u2)const
		{
			double r;
			int retVal = TRUE;
			if (p < 0.0)
			{
				r = q / p;
				if (r > *u2)
					retVal = FALSE;
				else if (r > *u1)
					*u1 = r;
			}
			else if (p > 0.0)
			{
				r = q / p;
				if (r < *u1)
					retVal = FALSE;
				else if (r < *u2)
					*u2 = r;
			}
			else if (q < 0.0)
				/* p = 0, so line is parallel to this clipping edge */
				/* Line is outside clipping edge */
				retVal = FALSE;
			return (retVal);
		}
		bool clipLine(PointT2 &p1, PointT2 &p2) const
		{
			double u1 = 0.0, u2 = 1.0, dx = p2.x - p1.x, dy;
			double x1 = m_ptMin.x;
			double y1 = m_ptMin.y;
			double x2 = m_ptMax.x;
			double y2 = m_ptMax.y;
			if (clip_test(-dx, p1.x - x1, &u1, &u2))
			{
				if (clip_test(dx, x2 - p1.x, &u1, &u2))
				{
					dy = p2.y - p1.y;
					if (clip_test(-dy, p1.y - y1, &u1, &u2))
					{
						if (clip_test(dy, y2 - p1.y, &u1, &u2))
						{
							if (u2 < 1.0)
							{
								p2.x = p1.x + u2 * dx;
								p2.y = p1.y + u2 * dy;
							}
							if (u1 > 0.0)
							{
								p1.x += u1 * dx;
								p1.y += u1 * dy;
							}
							return true;
						}
					}
				}
			}
			return false;
		}
	};
	typedef TGeBoundBox2<GePoint2d,double>GeBoundBox2d;

}